package top.lingkang.finaloauth2.server.store.impl;

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import top.lingkang.finaloauth2.entity.*;
import top.lingkang.finaloauth2.server.store.TokenStore;

import java.util.concurrent.TimeUnit;

/**
 * @author lingkang
 * Created by 2022/4/6
 */
public class RedisTokenStore implements TokenStore {
    private RedisTemplate redisTemplate;
    private static final String prefix_token = "t.";
    private static final String prefix_user = "u.";
    private static final String prefix_refresh = "r.";

    public RedisTokenStore(RedisTemplate redisTemplate) {
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        this.redisTemplate = redisTemplate;
    }

    @Override
    public OAuth2AccessToken getAccessToken(String token) {
        Object o = redisTemplate.opsForValue().get(prefix_token + token);
        return o != null ? (OAuth2AccessToken) o : null;
    }

    @Override
    public UserTokenInfo getUserTokenInfo(String username, String clientId) {
        UserTokenInfo info = new UserTokenInfo();
        Object o = redisTemplate.opsForValue().get("_t." + clientId + "." + username);
        if (o != null) {
            String token = (String) o;
            Object o1 = redisTemplate.opsForValue().get(prefix_token + token);
            if (o1 != null) {
                OAuth2AccessToken accessToken = (OAuth2AccessToken) o1;
                info.setToken(token);
                info.setTokenExpires(accessToken.getExpiry());
            }
        } else {
            return null;
        }
        o = redisTemplate.opsForValue().get("_r." + clientId + "." + username);
        if (o != null) {
            String refreshToken = (String) o;
            Object o1 = redisTemplate.opsForValue().get(prefix_refresh + refreshToken);
            if (o1 != null) {
                OAuth2RefreshToken auth2RefreshToken = (OAuth2RefreshToken) o1;
                info.setRefreshToken(refreshToken);
                info.setRefreshExpires(auth2RefreshToken.getExpiry());
            }
        } else {
            return null;
        }
        return info;
    }

    @Override
    public OAuth2AccessToken removeAccessToken(String token) {
        Object o = redisTemplate.opsForValue().get(prefix_token + token);
        redisTemplate.delete(prefix_token + token);
        Object o1 = redisTemplate.opsForValue().get(prefix_user + token);
        if (o1 != null) {
            UserDetails userDetails = (UserDetails) o1;
            redisTemplate.delete("_t." + userDetails.getClientId() + "." + userDetails.getUsername());
        }
        // 删除用户详情
        redisTemplate.delete(prefix_user + token);
        return o != null ? (OAuth2AccessToken) o : null;
    }

    @Override
    public void storeAccessToken(OAuth2AccessToken accessToken, UserDetails userDetails) {
        long expiry = accessToken.getExpiry();
        long has = expiry - System.currentTimeMillis();
        redisTemplate.opsForValue().set(prefix_token + accessToken.getToken(), accessToken, has, TimeUnit.MILLISECONDS);
        redisTemplate.opsForValue().set(prefix_user + accessToken.getToken(), userDetails, has, TimeUnit.MILLISECONDS);
        // 用户与token关联
        redisTemplate.opsForValue().set("_t." + userDetails.getClientId() + "." + userDetails.getUsername(),
                accessToken.getToken(), has, TimeUnit.MILLISECONDS);
    }

    @Override
    public UserDetails getUserDetails(String token) {
        OAuth2AccessToken accessToken = getAccessToken(token);
        if (accessToken != null) {
            Object o = redisTemplate.opsForValue().get(prefix_user + token);
            if (o != null)
                return (UserDetails) o;
        }
        return null;
    }

    @Override
    public OAuth2RefreshToken getRefreshToken(String refreshToken) {
        Object o = redisTemplate.opsForValue().get(prefix_refresh + refreshToken);
        if (o != null) {
            return (OAuth2RefreshToken) o;
        }
        return null;
    }

    @Override
    public OAuth2RefreshToken removeRefreshToken(String refreshToken) {
        Object o = redisTemplate.opsForValue().get(prefix_refresh + refreshToken);
        redisTemplate.delete(prefix_refresh + refreshToken);
        if (o != null) {
            OAuth2RefreshToken auth2RefreshToken = (OAuth2RefreshToken) o;
            redisTemplate.delete("_r." + auth2RefreshToken.getUserDetails().getClientId()
                    + "." + auth2RefreshToken.getUserDetails().getUsername());
            return auth2RefreshToken;
        }
        return null;
    }

    @Override
    public void storeRefreshToken(OAuth2RefreshToken refreshToken) {
        long expiry = refreshToken.getExpiry();
        long has = expiry - System.currentTimeMillis();
        redisTemplate.opsForValue().set(prefix_refresh + refreshToken.getRefreshToken(), refreshToken, has, TimeUnit.MILLISECONDS);
        // 用户与 refreshToken 关联
        redisTemplate.opsForValue().set("_r." + refreshToken.getUserDetails().getClientId() + "."
                + refreshToken.getUserDetails().getUsername(), refreshToken.getRefreshToken(), has, TimeUnit.MILLISECONDS);
    }

    @Override
    public void setCode(Oauth2Code oauth2Code) {
        long expiry = oauth2Code.getExpiry();
        long has = expiry - System.currentTimeMillis();
        redisTemplate.opsForValue().set("code." + oauth2Code.getCode(), oauth2Code, has, TimeUnit.MILLISECONDS);
    }

    @Override
    public Oauth2Code getOauth2Code(String code) {
        Object o = redisTemplate.opsForValue().get("code." + code);
        if (o != null) {
            Oauth2Code oauth2Code = (Oauth2Code) o;
            redisTemplate.delete("code." + code);
            return oauth2Code;
        }
        return null;
    }
}
